package com.dmp.onlinestore.model.dao.jdbc.base;

import com.dmp.onlinestore.system.Settings;
import com.dmp.onlinestore.model.dao.base.DaoException;
import com.dmp.onlinestore.model.dao.base.DaoTransaction;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class JdbcDaoTransaction implements DaoTransaction {
	
//	public JdbcProductDao () {
//	DriverUtilities.loadDrivers();
//	DB_URL = DriverUtilities.makeURL("localhost", "onlinestore" , DriverUtilities.MYSQL);
//}

	
	
	// The underlying JDBC connection.
	private Connection connection;
	
	// True if this is an update transaction. Updated to true when the
	// first update statement within the context of this transaction is executed.
	boolean updateTransaction = false;
	
	// Holds value of the auto commit prior to the start of this transaction.
	boolean previousAutoCommit;

	
	public JdbcDaoTransaction () throws DaoException {
		// We open the connection as part of the process of creating
		// the transaction.
		openConnection();
	}
	
	// Opens the connection.
    // TODO Update this to get the connection using Pedgram's database utilities.
	public void openConnection() throws DaoException {
		if (Settings.CONSOLE_DEBUG_ON) {
			System.out.println("JdbcDaoTransaction::openConnection");
		}
		if (getConnection() != null) {
			throw new DaoException("Attempt to open a JDBC connection on already-open connection.");
		}
		try {
			String driverName = "com.mysql.jdbc.Driver";
			Class.forName(driverName);
			Connection conn = 
				DriverManager.getConnection("jdbc:mysql://localhost:3306/onlinestore?" +
				                            "user=root&password=password");
			setConnection(conn);
		}
		catch (SQLException e) {
			throw new DaoException("Exception creating JDBC connection.");
		}
		catch (ClassNotFoundException e) {
			throw new DaoException("Exception loading JDBC driver.");
		}
	}
	
	// Closes the connection.
	public void closeConnection() throws DaoException {
		if (Settings.CONSOLE_DEBUG_ON) {
			System.out.println("JdbcDaoTransaction::closeConnection");
		}
		if (getConnection() != null) {
			// Close the connection.
			try {
				getConnection().close();
				setConnection(null);
			}
			catch (SQLException e){
				throw new DaoException("Error closing JDBC connection.");
			}			
		}
	}
	
	public void setConnection(Connection conn) {
		this.connection = conn;
	}
	public Connection getConnection() {
		return this.connection;
	}
	
	// Flags this transaction as involving a database update. Throws a DaoException if
	// a connection has not yet been allocated. Used in rollback and commit logic.
	public void flagUpdate() throws DaoException {
		if (Settings.CONSOLE_DEBUG_ON) {
			System.out.println("JdbcDaoTransaction::flagUpdate");
		}
		if (getConnection() == null) {
			throw new DaoException("Flagging a transaction as an update transaction with no open connection.");
		}
		if (! isUpdateTransaction()) {
			// First time doing this for this transaction.
			if (Settings.CONSOLE_DEBUG_ON) {
				System.out.println("JdbcDaoTransaction::flagUpdate-disableAutoCommit");
			}			
			this.updateTransaction = true;
			try {
				setPreviousAutoCommit(getConnection().getAutoCommit());
				getConnection().setAutoCommit(false);
			}
			catch (SQLException e) {
				// Error. Take everything down.
				fail();
			}
		}
	}
	
	public void setPreviousAutoCommit(boolean value) {
		this.previousAutoCommit = value;
	}
	public boolean getPreviousAutoCommit() {
		return this.previousAutoCommit;
	}
	
	// True if this is an update transaction.
	private boolean isUpdateTransaction() {
		return this.updateTransaction;
	}
	
	// Invoked at the end of a transaction. Handles commits if necessary and
	// cleans things up.
	public void finish() throws DaoException {
		if (Settings.CONSOLE_DEBUG_ON) {
			System.out.println("JdbcDaoTransaction::finish");
		}
		
		if (getConnection() == null) {
			throw new DaoException("Attempt to finish a transaction with no open connection.");
		}
		StringBuffer errorString = new StringBuffer("");
		if (isUpdateTransaction()) {
			// Handle commit logic.
			try {
				if (Settings.CONSOLE_DEBUG_ON) {
					System.out.println("JdbcDaoTransaction::finish-commit");
				}
				// Handle commit logic.
				getConnection().commit();
			}
			catch (SQLException e) {
				errorString.append("commit");
				fail(); // try a rollback
			}
			// Reset autocommit.
			try {
				if (Settings.CONSOLE_DEBUG_ON) {
					System.out.println("JdbcDaoTransaction::finish-restoreAutoCommit");
				}
				getConnection().setAutoCommit(getPreviousAutoCommit());				
			}
			catch (SQLException e) {
				if (errorString.length() > 0) {
					errorString.append(",commitReset");
				}
				else {
					errorString.append("commitReset");
				}
			}
		}
		try {
			closeConnection();
			setConnection(null);
		}
		catch (DaoException e) {
			if (errorString.length() > 0) {
				errorString.append(",close");
			}
			else {
				errorString.append("close");
			}
		}
		if (errorString.length() > 0) {
			throw new DaoException("Error(s) finishing transaction: " + errorString.toString());
		}
	}
	
	// Fails the transaction. Does any required rollbacks, cleans up any required resources.
	public void fail() {
		if (Settings.CONSOLE_DEBUG_ON) {
			System.out.println("JdbcDaoTransaction::fail");
		}
		if (getConnection() != null) {
			if (isUpdateTransaction()) {
				try {
					// Handle rollback logic.
					if (Settings.CONSOLE_DEBUG_ON) {
						System.out.println("JdbcDaoTransaction::fail-rollback");
					}
					getConnection().rollback();
				}
				catch (SQLException e) {
				}
				// Reset autocommit.
				try {
					if (Settings.CONSOLE_DEBUG_ON) {
						System.out.println("JdbcDaoTransaction::fail-restoreAutoCommit");
					}
					getConnection().setAutoCommit(getPreviousAutoCommit());				
				}
				catch (SQLException e) {
				}
			}
			try {
				closeConnection();
				setConnection(null);
			}
			catch (DaoException e) {
			}
		}
	} // fail
}
